package com.jason.web.support.signature.security;

/**
 * Created by junlee on 2017/10/27.
 */
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.*;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 * http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/md5.js
 * </p>
 */
public class RSAUtils {

	public static final String KEY_ALGORITHM = "RSA";
	public static final String SIGNATURE_ALGORITHM = "MD5withRSA";
	private static final String PUBLIC_KEY = "RSAPublicKey";
	private static final String PRIVATE_KEY = "RSAPrivateKey";
	
	private static final String publicKey ="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDCPhb7ygWnCaky7CxZIYjRb23ixuYQJTX3XkoS\n" +
			"NzpD3z1zLoSW50rjliShaEI/cbOHtLPWIumun5ezBdcIpQ9sdbFeP0SCva7qCHrC12dB5n6rccXG\n" +
			"ZLMWTJhHUuOBE3hqiuiekfzkuKkRFLVoX8FPKBR0IJ8bqNj5ce3LztDOPQIDAQAB";
	
	private static final String privateKey ="MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMI+FvvKBacJqTLsLFkhiNFvbeLG\n" +
			"5hAlNfdeShI3OkPfPXMuhJbnSuOWJKFoQj9xs4e0s9Yi6a6fl7MF1wilD2x1sV4/RIK9ruoIesLX\n" +
			"Z0HmfqtxxcZksxZMmEdS44ETeGqK6J6R/OS4qREUtWhfwU8oFHQgnxuo2Plx7cvO0M49AgMBAAEC\n" +
			"gYBbZkf+VsNuJBefxI3hquvsUNiOGzVqZ3gLXCzSgXZDNap+pmbxcwhEKwQ1hw+VNvVZZ+gvXOJE\n" +
			"rwf8z0St5q1oc+JJkU0gbQBoZJURAzX627iqLQsQ0zrj5X0sjBAnNoBGEpH9W8KNFp33FhLNwUZo\n" +
			"dNdom1+368tExw0HnclngQJBAOE3iXYObrKOFxURjKOvWDz1eRCHzplkG+bddEQAPd4oVdfTOk8D\n" +
			"lu+WDjmzRrDzQ8GU4HJKhF1w6p18VpZO0w0CQQDcyr3cXX7ujDdwni2mNCbQLMCzyAWnttpiRHqN\n" +
			"8aFKmhu/r0SRmxvNERqyakhu7qNjtc53H4R0Tl5WpQmcUNvxAkBqDosvCxDaCMuZ4k/GbTYyTq6a\n" +
			"ODn0Rg5Vo4ey1iJMUdZK8m+M8fXMEW/VgDUT8xq/OGqRp8G5dwkk7H6h/bCpAkEAq3QU86b3Bq+D\n" +
			"7cQnlsUwzr+x9OoC2jY4Z87djJRyt8fsfHHSQW+9qwT5QksebGaMIsXvyrmC1f51a7tFN0njQQJB\n" +
			"AKQEehLw+c8PyX52wujjX5mypTixSSQcSfiH7vgmB+t8YZu2tKNxkPoTKMOLmObHcppwdnLOHbga\n" +
			"X0U25EFjiyc=";
	
	
	private static Logger logger = LoggerFactory.getLogger(RSAUtils.class);
	
	/**
	 * @return
	 * @throws Exception
	 */
	public static Map<String, Object> genKeyPair() throws Exception {
		KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
		keyPairGen.initialize(1024);
		KeyPair keyPair = keyPairGen.generateKeyPair();
		RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
		RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
		Map<String, Object> keyMap = new HashMap<String, Object>(2);
		keyMap.put(PUBLIC_KEY, publicKey);
		keyMap.put(PRIVATE_KEY, privateKey);
		return keyMap;
	}
	
	public static String sign(byte[] data, String privateKey) throws Exception {
		byte[] keyBytes = Base64Utils.decode(privateKey);
		PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
		Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
		signature.initSign(privateK);
		signature.update(data);
		return Base64Utils.encode(signature.sign()).trim();
	}
	
	public static boolean verify(byte[] data, String publicKey, String sign)  {
		try {
			byte[] keyBytes = Base64Utils.decode(publicKey);
			X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
			KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
			PublicKey publicK = keyFactory.generatePublic(keySpec);
			Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
			signature.initVerify(publicK);
			signature.update(data);
			return signature.verify(Base64Utils.decode(sign));
		}catch (Exception e){
			logger.error("验签失败，e={}",e);
		}
		return false;
	}
	
	/**
	 * * 加密 *
	 *
	 * @param data 待加密的明文数据 *
	 * @return 加密后的数据 *
	 * @throws Exception
	 */
	public static byte[] encryptByPublicKey(byte[] data, String publicKey) {
		byte[] keyBytes = Base64Utils.decode(publicKey);
		X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
		KeyFactory keyFactory ;
		Key publicK;
		try {
			keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
			publicK = keyFactory.generatePublic(x509KeySpec);
			Cipher cipher = Cipher.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());
			cipher.init(Cipher.ENCRYPT_MODE, publicK);
			// 获得加密块大小，如：加密前数据为128个byte，而key_size=1024
			int blockSize = cipher.getBlockSize();
			// 加密块大小为127
			// byte,加密后为128个byte;因此共有2个加密块，第一个127
			// byte第二个为1个byte
			int outputSize = cipher.getOutputSize(data.length);// 获得加密块加密后块大小
			int leavedSize = data.length % blockSize;
			int blocksSize = leavedSize != 0 ? data.length / blockSize + 1 : data.length / blockSize;
			byte[] raw = new byte[outputSize * blocksSize];
			int i = 0;
			while (data.length - i * blockSize > 0) {
				if (data.length - i * blockSize > blockSize) {
					cipher.doFinal(data, i * blockSize, blockSize, raw, i * outputSize);
				} else {
					cipher.doFinal(data, i * blockSize, data.length - i * blockSize, raw, i * outputSize);
				}
				i++;
			}
			return raw;
		} catch (Exception e) {
			logger.error("加密数据失败，e={}",e);
		}
		return null;
	}
	
	/**
	 * * 解密
	 *
	 * @param raw 已经加密的数据 *
	 * @return 解密后的明文 *
	 * @throws Exception
	 */
	@SuppressWarnings("static-access")
	public static byte[] decryptByPrivateKey(byte[] raw, String privateKey) throws Exception {
		byte[] keyBytes = Base64Utils.decode(privateKey);
		PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
		try {
			Cipher cipher = Cipher.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());
			cipher.init(cipher.DECRYPT_MODE, privateK);
			int blockSize = cipher.getBlockSize();
			ByteArrayOutputStream bout = new ByteArrayOutputStream(64);
			int j = 0;
			while (raw.length - j * blockSize > 0) {
				bout.write(cipher.doFinal(raw, j * blockSize, blockSize));
				j++;
			}
			
			return bout.toByteArray();
		} catch (Exception e) {
			throw new Exception(e.getMessage(), e);
		}
	}
	
	public static String getPrivateKey(Map<String, Object> keyMap) throws Exception {
		Key key = (Key) keyMap.get(PRIVATE_KEY);
		return Base64Utils.encode(key.getEncoded());
	}
	
	public static String getPublicKey(Map<String, Object> keyMap) throws Exception {
		Key key = (Key) keyMap.get(PUBLIC_KEY);
		return Base64Utils.encode(key.getEncoded());
	}
	
	
	
	public static void main(String[] args) throws Exception {
		
		//Map<String, Object> stringObjectMap = RSAUtils.genKeyPair();
		//String publicKey = RSAUtils.getPublicKey(stringObjectMap);
		//String privateKey = RSAUtils.getPrivateKey(stringObjectMap);
		//Map<String,String> map = new HashMap<>();
		//map.put("publickey",publicKey);
		//map.put("privateKey",privateKey);
		//System.out.println("-----publickey---->" + publicKey);
		//System.out.println("-----privateKey---->" + privateKey);
		
		byte[] bytes1 = RSAUtils.encryptByPublicKey("test-lijun-李俊".getBytes(), publicKey);
		
		byte[] bytes2 = RSAUtils.decryptByPrivateKey(bytes1, privateKey);
		
		System.out.println("-----加密字符串解密---->" + new String(bytes2));
		
		System.out.println("pub-encode: " + Base64Utils.encode(bytes1));
		
		System.out.println("-----加密字符串长度----->" + bytes1.length);
		
		String signStr = sign(bytes1,privateKey);
		
		System.out.println("-----加密字符串的的签名---->  sign=" + signStr);
		
		Boolean flag =verify(bytes1,publicKey,signStr);
		System.out.println("-----加密串的验签结果---->  sign=  " + flag );
		
		System.out.println("-----加密串的验签结果---- ");
	System.out.print(verification("test-lijun-李俊","ueHjZiPEmxZ87Zl9qLnvQFntJQPLC4kBf7Q2eC2G5acZKeym2lvRkq4oG2wgfVGrnME3NJ3X58ly\n" +
			"+ESGXAKYi07FxW00pFSrnlHfkKmV5OJ1vB7oQV8W6uI5f1TDbKqDndVqU5Zl2s/pYW0Df5BiM+SI\n" +
			"EU0sY7xeJiwvZe35bq8="));
	}
	
	public static  Boolean verification(final String busData,final String signStr ){
		try {
			if (busData.length() > 0 && signStr.length() > 0) {
				byte[] busDataBytes = RSAUtils.encryptByPublicKey(busData.getBytes(), publicKey);
				return verify(busDataBytes, publicKey, signStr);
			} else {
				return false;
			}
		}catch (Exception e){
			logger.error("验签出错，e={}",e);
		}
		return false;
	}

}

